package com.zwps.common.mybatis.plus.interceptor;

import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import com.zwps.common.mybatis.context.ThreadLocalSkipDataPermissionContext;
import com.zwps.common.tool.function.FunT0;

import cn.hutool.core.collection.CollectionUtil;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.update.Update;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
 * 
 * @ClassName: AbstractDataPermissionInterceptor
 * @Description:
 * @author lyh
 * @date 2023年9月21日 下午4:58:32
 *
 */
public abstract class AbstractDataPermissionInterceptor extends TenantLineInnerInterceptor {

    public abstract Map<String, List<Function<Table, Expression>>> getTables();

    public void addTableExpressionList(String tableName, List<Function<Table, Expression>> list) {
        getTables().put(tableName, list);
    }

    public List<Function<Table, Expression>> getTableExpressionList(String tableName) {
        return getTables().get(tableName);
    }

    @Override
    protected void processDelete(Delete delete, int index, String sql, Object obj) {
        Table table = delete.getTable();
        List<Function<Table, Expression>> fieldValueList = checkTable(getTables(), table.getName());
        if (fieldValueList == null) {
            // 过滤退出执行
            return;
        }
        for (Function<Table, Expression> expressionFun : fieldValueList) {
            if (expressionFun != null) {
                Expression expression = expressionFun.apply(table);
                if (expression != null) {
                    delete.setWhere(andExpression(table, delete.getWhere(), (String) obj, expression));
                }
            }
        }
    }

    /**
     * 由于mybatis-plus select代码逻辑太复杂，只提取添加租户策略部分代码进行修改
     */
    @Override
    public Expression buildTableExpression(final Table table, final Expression where, final String whereSegment) {
        List<Function<Table, Expression>> fieldValueList = checkTable(getTables(), table.getName());
        if (fieldValueList == null) {
            // 过滤退出执行
            return null;
        }
        Expression expression = null;

        for (Function<Table, Expression> expressionFun : fieldValueList) {
            Expression exp;
            if (expressionFun == null || (exp = expressionFun.apply(table)) == null) {
                continue;
            }
            // 如果存在id查询，并且当前过滤条件也存在id，则跳过当前id条件注入
            // 为了解决某些地方通过id是能查询到信息
            if (existId(exp.toString()) && where != null && existId(where.toString())) {
                continue;
            }
            if (expression == null) {
                expression = exp;
            } else {
                new AndExpression(expression, exp);
            }
        }
        return expression;
    }

    private boolean existId(String expStr) {
        return expStr.contains("id ") || expStr.contains(" id ") || expStr.contains(".id ");
    }
    @Override
    protected void processUpdate(Update update, int index, String sql, Object obj) {
        Table table = update.getTable();
        List<Function<Table, Expression>> fieldValueList = checkTable(getTables(), table.getName());
        if (fieldValueList == null) {
            // 过滤退出执行
            return;
        }
        for (Function<Table, Expression> expressionFun : fieldValueList) {
            if (expressionFun != null) {
                Expression expression = expressionFun.apply(table);
                if (expression != null) {
                    update.setWhere(andExpression(table, update.getWhere(), (String) obj, expression));
                }
            }
        }
    }

    /**
     * delete update 语句 where 处理
     */
    protected Expression andExpression(Table table, Expression where, final String whereSegment,
            Expression expression) {
        if (where != null) {
            if (where instanceof OrExpression) {
                return new AndExpression(new Parenthesis(where), expression);
            } else {
                return new AndExpression(where, expression);
            }
        }
        return expression;
    }

    @Override
    protected void processInsert(Insert insert, int index, String sql, Object obj) {

    }

    protected List<Function<Table, Expression>> checkTable(Map<String, List<Function<Table, Expression>>> fillTableMap,
            String tableName) {
        List<Function<Table, Expression>> fieldValueList = fillTableMap.get(tableName);
        if (fieldValueList == null || fieldValueList.isEmpty()) {
            return null;
        }
        return fieldValueList;
    }

    public void addWhereIn(String tableName, String columnName, FunT0<Collection<String>> val) {
        List<Function<Table, Expression>> list = getTableExpressionList(tableName);
        if (list == null) {
            addTableExpressionList(tableName, list = new ArrayList<Function<Table, Expression>>());
        }
        list.add((table) -> {
            if (ThreadLocalSkipDataPermissionContext.skip(tableName, columnName)) {
                return null;
            }
            Collection<String> vs = val.apply();
            if (CollectionUtil.isEmpty(vs)) {
                return null; // 返回null表示最终不会拼接查询条件
            }
            return buildInExpression(table, columnName, vs);
        });
    }

    public void addWhereIn(String tableName, String columnName, Collection<String> val) {
        List<Function<Table, Expression>> list = getTableExpressionList(tableName);
        if (list == null) {
            addTableExpressionList(tableName, list = new ArrayList<Function<Table, Expression>>());
        }
        list.add((table) -> {
            if (ThreadLocalSkipDataPermissionContext.skip(tableName, columnName)) {
                return null;
            }
            if (CollectionUtil.isEmpty(val)) {
                return null; // 返回null表示最终不会拼接查询条件
            }
            return buildInExpression(table, columnName, val);
        });
    }

    private InExpression buildInExpression(Table table, String columnName, Collection<String> val) {
        ExpressionList expressionList = new ExpressionList();
        for (String v : val) {
            expressionList.addExpressions(new StringValue(v));
        }
        InExpression inExpression = new InExpression();
        inExpression.setLeftExpression(getAliasColumn(table, columnName));
        inExpression.setRightItemsList(expressionList);
        return inExpression;
    }

    public void addWhereEq(String tableName, String columnName, FunT0<String> val) {
        List<Function<Table, Expression>> list = getTableExpressionList(tableName);
        if (list == null) {
            addTableExpressionList(tableName, list = new ArrayList<Function<Table, Expression>>());
        }
        list.add((table) -> {
            if (ThreadLocalSkipDataPermissionContext.skip(tableName, columnName)) {
                return null;
            }
            String v = val.apply();
            if (v == null) {
                return null; // 返回null表示最终不会拼接查询条件
            }
            return buildEqualsTo(columnName, v);
        });
    }

    public void addWhereEq(String tableName, String columnName, String val) {
        List<Function<Table, Expression>> list = getTableExpressionList(tableName);
        if (list == null) {
            addTableExpressionList(tableName, list = new ArrayList<Function<Table, Expression>>());
        }
        list.add((table) -> {
            if (ThreadLocalSkipDataPermissionContext.skip(tableName, columnName)) {
                return null;
            }
            if (val == null) {
                return null; // 返回null表示最终不会拼接查询条件
            }
            return buildEqualsTo(columnName, val);
        });
    }

    private EqualsTo buildEqualsTo(String columnName, String val) {
        EqualsTo equalsTo = new EqualsTo();
        equalsTo.setLeftExpression(new StringValue(columnName));
        equalsTo.setRightExpression(new StringValue(val));
        return equalsTo;
    }

    protected Column getAliasColumn(Table table, String columnName) {
        StringBuilder column = new StringBuilder();
        // 禁止 `为了兼容隐式内连接，没有别名时条件就需要加上表名`
        // 该起别名就要起别名
        if (table.getAlias() != null) {
            column.append(table.getAlias().getName());
        } else {
            column.append(table.getName());
        }
        column.append(StringPool.DOT).append(columnName);
        return new Column(column.toString());
    }
}
